﻿/****************************************************************************************************
	Copyright (C) 2010 RapidWebDev Organization (http://rapidwebdev.org)
	Author: Eunge, Legal Name: Jian Liu, Email: eunge.liu@RapidWebDev.org

	The GNU Library General Public License (LGPL) used in RapidWebDev is 
	intended to guarantee your freedom to share and change free software - to 
	make sure the software is free for all its users.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Library General Public License (LGPL) for more details.

	You should have received a copy of the GNU Library General Public License (LGPL)
	along with this program.  
	If not, see http://www.rapidwebdev.org/Content/ByUniqueKey/OpenSourceLicense
 ****************************************************************************************************/

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using RapidWebDev.FileManagement.Properties;

namespace RapidWebDev.FileManagement.Thumbnails
{
    /// <summary>
    /// The thumbnail utilities class
    /// </summary>
    internal static class ThumbnailUtilities
    {
        /// <summary>
        /// Validate thumbnail size utility method.
        /// </summary>
        /// <param name="thumbnailSize">Thumbnail size</param>
        public static void ValidateThumbnailSize(Size thumbnailSize)
        {
            if (thumbnailSize.Width <= 0 || thumbnailSize.Height <= 0)
                throw new ArgumentOutOfRangeException("thumbnailSize", Resources.ThumbnailHeightWidthShouldGreaterZero);
        }

        /// <summary>
        /// Calculate actual thumbnail size.
        /// </summary>
        /// <param name="fileSize">File size</param>
        /// <param name="expectedThumbnailSize">Expected thumbnail size</param>
        /// <returns>Actual thumbnail size</returns>
        public static Size CalculateActualThumbnailSize(Size fileSize, Size expectedThumbnailSize)
        {
            Single fileWidth = Convert.ToSingle(fileSize.Width);
            Single fileHeight = Convert.ToSingle(fileSize.Height);
            Single expectedWidth = Convert.ToSingle(expectedThumbnailSize.Width);
            Single expectedHeight = Convert.ToSingle(expectedThumbnailSize.Height);

            if (fileHeight <= expectedHeight && fileWidth <= expectedWidth)
            {
                return fileSize;
            }
            Size actualThumbnailSize = new Size();
            if (fileHeight / fileWidth > expectedHeight / expectedWidth)
            {
                actualThumbnailSize.Height = (int)expectedHeight;
                actualThumbnailSize.Width = (int)(fileWidth * expectedHeight / fileHeight);
                if (actualThumbnailSize.Width <= 0) actualThumbnailSize.Width = 1;
            }
            else
            {
                actualThumbnailSize.Width = (int)expectedWidth;
                actualThumbnailSize.Height = (int)(fileHeight * expectedWidth / fileWidth);
                if (actualThumbnailSize.Height <= 0) actualThumbnailSize.Height = 1;
            }
            return actualThumbnailSize;
        }

        /// <summary>
        /// Calculate actual thumbnail location.
        /// </summary>
        /// <param name="actualThumbnailSize">Actual thumbnail location</param>
        /// <param name="expectedThumbnailSize">Expected thumbnail size</param>
        /// <returns>The left top point of the actual thumbnail should be located in expected thumbnail</returns>
        public static Point CalculateActualThumbnailLocation(Size actualThumbnailSize, Size expectedThumbnailSize)
        {
            return new Point((expectedThumbnailSize.Width - actualThumbnailSize.Width) / 2, (expectedThumbnailSize.Height - actualThumbnailSize.Height) / 2);
        }

        /// <summary>
        /// Calculate thumbnail scale compare to the original file.
        /// </summary>
        /// <param name="fileSize">File size</param>
        /// <param name="expectedThumbnailSize">Expected thumbnail size</param>
        /// <returns>Scale</returns>
        public static Single CalculateThumbnailScale(Size fileSize, Size expectedThumbnailSize)
        {
            if (fileSize.Height <= expectedThumbnailSize.Height && fileSize.Width <= expectedThumbnailSize.Width)
            {
                return 1L;
            }

            Single fileWidth = Convert.ToSingle(fileSize.Width);
            Single fileHeight = Convert.ToSingle(fileSize.Height);
            Single expectedWidth = Convert.ToSingle(expectedThumbnailSize.Width);
            Single expectedHeight = Convert.ToSingle(expectedThumbnailSize.Height);

            if (fileHeight / fileWidth > expectedHeight / expectedWidth)
            {
                return expectedHeight / fileHeight;
            }
            else
            {
                return expectedWidth / fileWidth;
            }
        }

        /// <summary>
        /// Resize the thumbnail image.
        /// </summary>
        /// <param name="sourceImage">Source image</param>
        /// <param name="actualThumbnailSize">Actual thumbnail size</param>
        /// <param name="expectedThumbnailSize">Expected thumbnail size</param>
        /// <returns>Thumbnail image</returns>
        public static Image ResizeThumbnailImage(Image sourceImage, Size actualThumbnailSize, Size expectedThumbnailSize)
        {
            Image thumbnailImage = new Bitmap(expectedThumbnailSize.Width, expectedThumbnailSize.Height);
            using (Graphics graphicsMem = Graphics.FromImage(thumbnailImage))
            {
                Point location = ThumbnailUtilities.CalculateActualThumbnailLocation(actualThumbnailSize, expectedThumbnailSize);
                graphicsMem.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
                graphicsMem.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                graphicsMem.Clear(Color.White);
                graphicsMem.DrawImage(sourceImage, new Rectangle(location, actualThumbnailSize));
            }
            return thumbnailImage;
        }

        /// <summary>
        /// Utilities method to get all bytes from a stream, no matter the stream is seekable or not.
        /// </summary>
        /// <param name="stream">Stream, can be seekable or not</param>
        /// <returns>All bytes</returns>
        public static byte[] GetAllBytesFromStream(Stream stream)
        {
            Stream seekableStream = null;
            if (stream.CanSeek)
            {
                seekableStream = stream;
            }
            else
            {
                int length = 1024;
                byte[] buffer = new byte[length];
                MemoryStream memoryStream = new MemoryStream();

                int returnByteLength = 0;
                while ((returnByteLength = stream.Read(buffer, 0, length)) > 0)
                {
                    memoryStream.Write(buffer, 0, returnByteLength);
                }
                seekableStream = memoryStream;
            }

            byte[] bytes = new byte[seekableStream.Length];
            seekableStream.Read(bytes, 0, bytes.Length);
            if (!stream.CanSeek)
            {
                seekableStream.Dispose();
            }
            return bytes;
        }

        /// <summary>
        /// copy the data from source stream to destination stream
        /// </summary>
        /// <param name="sourceStream">source stream</param>
        /// <param name="destinationStream">destination stream</param>
        /// <returns>successful?</returns>
        public static bool TransferStream(Stream sourceStream, Stream destinationStream)
        {
            bool result = false;

            // 12k
            int bufferSize = 12288;

            if (sourceStream == null || destinationStream == null)
                return result;

            if (sourceStream.CanRead == false)
                return result;

            if (destinationStream.CanWrite == false)
                return result;

            byte[] buffer = new byte[bufferSize];

            int readbytes = 0;
            while ((readbytes = sourceStream.Read(buffer, 0, bufferSize)) > 0)
            {
                destinationStream.Write(buffer, 0, readbytes);
            }

            result = true;
            return result;
        }

        /// <summary>
        /// Get the extension name of the file
        /// </summary>
        /// <param name="fileName">file name</param>
        /// <returns>the extension name</returns>
        public static string GetFileExtension(string fileName)
        {
            string extensionName = Path.GetExtension(fileName);
            if (extensionName.StartsWith(".", StringComparison.Ordinal))
            {
                return extensionName.Substring(1, extensionName.Length - 1);
            }
            else
                return extensionName;
        }

        /// <summary>
        /// clear up the input size parameter for the function method
        /// </summary>
        /// <param name="sizes">the input size</param>
        /// <param name="totalNumber">the output total number of the size array</param>
        /// <returns>the size array cleaned up</returns>
        public static Size[] GetSizeArrayForInputParameter(Size[] sizes, int totalNumber)
        {
            Size[] value = null;

            if (totalNumber < 0)
                return value;

            IList<Size> sizeArray = new List<Size>(totalNumber);

            for (int index = 0; index < totalNumber; index++)
            {
                if (sizes != null && sizes.Length > index)
                    sizeArray.Add(sizes[index]);
                else
                    sizeArray.Add(new Size(256, 256));
            }
            value = sizeArray.ToArray();

            return value;
        }

        /// <summary>
        /// Save the bitmap as file.
        /// </summary>
        /// <param name="sourceImage"></param>
        /// <param name="savedFileName">Output file name.</param>
        public static void SaveImageToFile(Image sourceImage, string savedFileName)
        {
            if (sourceImage == null) return;
            if (string.IsNullOrEmpty(savedFileName)) return;
            if (System.IO.File.Exists(savedFileName))
                System.IO.File.Delete(savedFileName);

            sourceImage.Save(savedFileName, ImageFormat.Jpeg);
        }

        /// <summary>
        /// Gets the encoder information for the specified mimetype.  Used in imagescaling
        /// </summary>
        /// <param name="mimeType">The mimetype of the picture. etc image/jpeg, image/gif</param>
        /// <returns>System.Drawing.Imaging.ImageCodecInfo</returns>
        private static ImageCodecInfo GetEncoderInfo(string mimeType)
        {
            if (string.IsNullOrEmpty(mimeType)) return null;
            ImageCodecInfo[] myEncoders = ImageCodecInfo.GetImageEncoders();

            foreach (ImageCodecInfo myEncoder in myEncoders)
                if (myEncoder.MimeType == mimeType)
                    return myEncoder;
            return null;
        }
    }
}
